/*
 *
 *  * // Copyright (c) 2024 KylinOS. All rights reserved.
 *  * //
 *  * // This program is free software; you can redistribute it
 *  * // and/or modify it under the terms of the GNU General Public
 *  * // License (version 2) as published by the FSF - Free Software
 *  * // Foundation*
 *
 */

/*
 *  * // Copyright (C) 2024 ben LG <guoliben@kylinos.cn>
 */

package ppttask

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
)

// 定义OLE相关常量
const (
	OleSignature          = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
	DirSectOffset         = 0x30 // 目录扇区偏移位置
	HeaderSize            = 512  // OLE头大小
	DirEntrySize          = 128  // 目录项大小
	SectorSize            = 512  // 扇区大小
	MaxDirectoryEntries   = 100  // 最大目录项扫描数
	PowerPointSignature   = "PowerPoint Document"
	ContainerSignature    = "\x0F"
	AtomSignature         = "\x10\x00"
	PersiAtomSignature    = "\x00\x00\x72\x17"
	UserEditAtomSignature = "\x00\x00\xF5\x0F"
)

// OLE 目录项结构
type DirectoryEntry struct {
	Name        string
	StreamStart uint32
	StreamSize  uint32
	ObjectType  byte
	NameLength  uint16
}

// OLE Powerpoint Document数据流文件头
type PowerPointDucumentHeaders struct {
	Types  uint16
	Length uint32
}

func ProcessFile(path string) ([]byte, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println("Error opening ppt file:", err)
		return nil, err
	}
	text, err := ProcessBuffer(content)
	if err != nil {
		return nil, err
	}
	return text, nil
}

func ProcessBuffer(data []byte) ([]byte, error) {
	if len(data) > 0 {
		text, err := extractPptText(data)
		if err != nil {
			fmt.Println("Error reading ppt file:", err)
			return nil, err
		}
		return text, nil
	}
	return nil, errors.New("No data")
}

/********以下是内部函数******/
// 提取文本
func extractPptText(data []byte) ([]byte, error) {
	// 1. 创建二进制流读取器
	reader := bufio.NewReader(bytes.NewReader(data))

	// 2. OLE格式验证
	if !isValidOLE(reader) {
		return nil, fmt.Errorf("非OLE格式文件")
	}

	reader = bufio.NewReader(bytes.NewReader(data[DirSectOffset:]))
	// 3. 获取目录流位置
	dirOffset, err := getDirectoryOffset(reader)
	if err != nil {
		return nil, fmt.Errorf("目录定位失败: %w", err)
	}

	reader = bufio.NewReader(bytes.NewReader(data[dirOffset:]))
	// 4. 查找PPT数据流
	pptStream, err := findPowerPointStream(reader)
	if err != nil {
		return nil, fmt.Errorf("PPT流查找失败: %w", err)
	}

	// 5. 解析PPT内容
	var totalText bytes.Buffer
	parsePptContent(&totalText, data[(int64(pptStream.StreamStart)+1)*SectorSize:], 1)
	return totalText.Bytes(), nil
}

// 提取 UTF-16 LE 文本内容
func parsePptContent(totalText *bytes.Buffer, data []byte, depth int) {
	if depth > 100 {
		return
	}
	buffer := bytes.NewBuffer(data)
	headerChunk := make([]byte, 8)
	for buffer.Len() > 8 {
		// 读取文件头
		if _, err := io.ReadFull(buffer, headerChunk); err != nil {
			break
		}
		headers := parsePowerPointDucumentHeaders(headerChunk)

		// 异常处理
		if depth <= 1 &&
			!bytes.Equal(headerChunk[:1], []byte(ContainerSignature)) &&
			!bytes.Equal(headerChunk[:2], []byte(AtomSignature)) &&
			!bytes.Equal(headerChunk[:4], []byte(PersiAtomSignature)) &&
			!bytes.Equal(headerChunk[:4], []byte(UserEditAtomSignature)) {
			break
		}

		// 读取文件内容
		contentChunk := make([]byte, headers.Length)
		if n, err := io.ReadFull(buffer, contentChunk); n <= 0 || (err != nil && err != io.EOF) {
			break
		}

		switch headers.Types {
		case 1006, 1036, 61442, 61443, 61444, 61453:
			parsePptContent(totalText, contentChunk, depth+1)
		case 4000:
			totalText.WriteString(decodeUTF16LE(contentChunk))
		case 4008:
			totalText.WriteString(decodeUTF8LE(contentChunk))
		}
	}
}

// 解析 UTF-16 LE 编码的字符串
func decodeUTF16LE(data []byte) string {
	u16 := make([]uint16, len(data)/2)
	_ = binary.Read(bytes.NewReader(data), binary.LittleEndian, &u16)
	var runes []rune
	for _, v := range u16 {
		if v == 0 {
			break
		}
		runes = append(runes, rune(v))
	}
	return string(runes)
}

// 解析 UTF-8 LE 编码的字符串
func decodeUTF8LE(data []byte) string {
	return string(data)
}

// 解析 OLE 目录项文件头
func parsePowerPointDucumentHeaders(headers []byte) PowerPointDucumentHeaders {
	types := binary.LittleEndian.Uint16(headers[2:4])
	length := binary.LittleEndian.Uint32(headers[4:8])

	return PowerPointDucumentHeaders{
		Types:  types,
		Length: length,
	}
}

// 解析 OLE 目录项
func parseDirectoryEntry(entry []byte) DirectoryEntry {
	name := decodeUTF16LE(entry[:64])
	streamStart := binary.LittleEndian.Uint32(entry[116:120])
	streamSize := binary.LittleEndian.Uint32(entry[120:124])
	objectType := entry[66]
	nameLength := binary.LittleEndian.Uint16(entry[64:66])

	return DirectoryEntry{
		Name:        name,
		StreamStart: streamStart,
		StreamSize:  streamSize,
		ObjectType:  objectType,
		NameLength:  nameLength,
	}
}

// ---------- 辅助函数 ----------
// OLE格式验证
func isValidOLE(reader *bufio.Reader) bool {
	header := make([]byte, len(OleSignature))
	n, _ := io.ReadFull(reader, header)
	return n >= len(OleSignature) && bytes.Equal(header, []byte(OleSignature))
}

// 获取目录偏移量
func getDirectoryOffset(reader *bufio.Reader) (int64, error) {
	buffer := make([]byte, 4)
	if _, err := io.ReadFull(reader, buffer); err != nil {
		return 0, err
	}
	dirOffset := int64(binary.LittleEndian.Uint32(buffer))
	return (dirOffset + 1) * HeaderSize, nil
}

// 查找PPT流
func findPowerPointStream(reader *bufio.Reader) (*DirectoryEntry, error) {
	for i := 0; i < MaxDirectoryEntries; i++ {
		entryData := make([]byte, DirEntrySize)
		if _, err := io.ReadFull(reader, entryData); err != nil {
			return nil, err
		}
		entry := parseDirectoryEntry(entryData)
		if entry.Name == PowerPointSignature {
			return &entry, nil
		}

	}
	return nil, fmt.Errorf("未找到PPT数据流")
}
